package com.itmumu.miniwechat.user.service.impl;

import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itmumu.miniwechat.common.utils.*;
import com.itmumu.miniwechat.user.domain.po.UserContact;
import com.itmumu.miniwechat.user.domain.vo.response.UserContactVo;
import com.itmumu.miniwechat.user.mapper.UserMapper;
import com.itmumu.miniwechat.user.service.UserContactService;
import com.itmumu.miniwechat.common.constants.RedisKey;
import com.itmumu.miniwechat.common.exception.BusinessException;
import com.itmumu.miniwechat.user.domain.dto.RegisterFormDto;
import com.itmumu.miniwechat.user.domain.dto.SearchContactVo;
import com.itmumu.miniwechat.user.domain.dto.TokenUserInfoDto;
import com.itmumu.miniwechat.user.domain.enums.*;
import com.itmumu.miniwechat.user.domain.po.User;
import com.itmumu.miniwechat.user.domain.vo.response.LoginQrcodeVo;
import com.itmumu.miniwechat.user.domain.vo.response.UserInfoVo;
import com.itmumu.miniwechat.user.service.UserService;
import jakarta.annotation.Resource;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author Frippler
 * @description 针对表【user(用户表)】的数据库操作Service实现
 * @createDate 2024-12-11 14:54:45
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {

    @Autowired
    private WxMpService wxMpService;

    @Autowired
    private UserContactService userContactService;

    @Resource
    private RedisComponent redisComponent;

    /**
     * 用户生成带参数登录二维码
     *
     * @return
     */
    @Override
    public LoginQrcodeVo generateLoginQrcode() throws WxErrorException {
        Integer code = this.generateLoginCode();

        // 生成临时二维码 6000秒
        WxMpQrCodeTicket ticket = wxMpService.getQrcodeService().qrCodeCreateTmpTicket(code, 6000);
        String ticketStr = ticket.getTicket();
        String qrCodeUrl = wxMpService.getQrcodeService().qrCodePictureUrl(ticket.getTicket());

        // 将ticket信息存入redis
        RedisUtils.set(RedisKey.getKey(RedisKey.LOGIN_TICKET + ticketStr), ticketStr, 6000, TimeUnit.SECONDS);

        LoginQrcodeVo loginQrcodeVo = new LoginQrcodeVo();
        loginQrcodeVo.setQrcodeUrl(qrCodeUrl);
        loginQrcodeVo.setTicket(ticketStr);
        loginQrcodeVo.setExpireSeconds(6000);

        return loginQrcodeVo;
    }

    /**
     * 用户注册逻辑
     *
     * @param user
     */
    @Override
    public void register(User user) {
        // 设置用户昵称，格式wxid_xxxxx
        user.setNickname("wxid_" + user.getOpenid());
        user.setLoginNumber("wxid_" + user.getOpenid());
        this.save(user);
    }

    @Override
    public LoginQrcodeVo generateEventCode() {
        // 1、随机生成4位数字
        String eventCode = null;

        // 判断redis中是否存在,确保事件码唯一
        while (!Objects.nonNull(eventCode)) {
            eventCode = RandomUtils.generateRandomNumber(4);
            String cacheEventCode = RedisUtils.get(RedisKey.getKey(RedisKey.LOGIN_EVENT_CODE + eventCode), String.class);
            if (Objects.isNull(cacheEventCode)) {
                break;
            }
        }

        // 保存事件码,有效期为5分钟
        RedisUtils.set(RedisKey.getKey(RedisKey.LOGIN_EVENT_CODE + eventCode), eventCode, 5, TimeUnit.MINUTES);

        LoginQrcodeVo loginQrcodeVo = new LoginQrcodeVo();
        loginQrcodeVo.setEventCode(eventCode);
        return loginQrcodeVo;
    }


    /**
     * 用户注册
     *
     * @param registerFormDto
     */
    @Override
    public void registerUser(RegisterFormDto registerFormDto) {
        // 校验游戏是否存在
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getEmail, registerFormDto.getEmail());
        User userInfo = this.getOne(wrapper);
        if (!Objects.isNull(userInfo)) {
            throw new RuntimeException("邮箱账号已经存在");
        }

        userInfo = new User();
        userInfo.setNickname(registerFormDto.getNickName());
        userInfo.setEmail(registerFormDto.getEmail());
        userInfo.setPassword(SecureUtil.md5(registerFormDto.getPassword()));

        // 生成用户账号
        String uuid = "wxid_" + RandomUtils.generateUUID();
        userInfo.setLoginNumber(uuid);

        this.save(userInfo);

        // TODO 生成会话
    }

    @Override
    public UserInfoVo login(String email, String password) {
        // 根据email查询用信息
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getEmail, email);

        User user = this.getOne(wrapper);
        if (Objects.isNull(user)) {
            throw new BusinessException("用户不存在");
        }

        if (!password.equals(user.getPassword())) {
            throw new BusinessException("密码错误");
        }

        if (Objects.equals(user.getStatus(), UserSatausEnum.DISABLE.getStatus())) {
            throw new BusinessException("账号已被禁用");
        }

        TokenUserInfoDto tokenUserInfoDto = getTokenUserInfoDto(user);
        // 校验客户端是否重复登录

        Long lastHeartBeat = redisComponent.getUserHeartBeat(tokenUserInfoDto.getUid());
        if (lastHeartBeat != null) {
            throw new BusinessException("此账号已经在别处登录，请退出后再登录");
        }

        // 初始化联系人，初始化自己的一条记录
        userContactService.initLoginUserContact(user.getUid());

        // TODO 查询用户联系人，将用户联系人进行redis缓存
        LambdaQueryWrapper<UserContact> friendWrapper = new LambdaQueryWrapper<>();
        friendWrapper
                .and(wp -> {
                    wp.eq(UserContact::getUid, user.getUid())
                            .or()
                            .eq(UserContact::getContactId, user.getUid());
                })
                .eq(UserContact::getContactStatus, UserContactStatus.FRIEND.getCode())
                .eq(UserContact::getIsBlocked, UserContactBlockEnum.NOT_BLOCKED)
                .eq(UserContact::getDeletedBy, 0)
                .orderBy(true, true, UserContact::getId);

        List<UserContact> userContacts = userContactService.list(friendWrapper);
        List<Map<String, Integer>> contactIds = userContacts.stream().map(item -> {
            Integer contactId = item.getContactId();
            Integer contactType = item.getContactType();

            Map<String, Integer> map = new HashMap<>();
            map.put("contactId", contactId);
            map.put("contactType", contactType);
            return map;
        }).collect(Collectors.toList());

        redisComponent.cleanUserContact(user.getUid());
        if (contactIds.size() > 0) {
            redisComponent. addUserContactBatch(user.getUid(), contactIds);
        }

        // 校验通过，说明登录成功, 发放token
        HashMap<String, Object> map = new HashMap<>();
        map.put("uid", user.getUid());
        map.put("username", user.getNickname());
        map.put("mobile", user.getMobile());
        map.put("email", user.getEmail());
        String token = JwtUtils.generateToken(map, 3);

        UserInfoVo userInfoVo = new UserInfoVo();
        userInfoVo.setToken(token);

        BeanUtils.copyProperties(user, userInfoVo);

        tokenUserInfoDto.setToken(token);

        // 存入redis
        redisComponent.saveTokenUserInfo(tokenUserInfoDto);
        return userInfoVo;
    }


    /**
     * 联系人页面--用户搜索联系人
     *
     * @param searchContactVo
     * @return
     */
    @Override
    public Integer searchContact(SearchContactVo searchContactVo) {
        String keyword = searchContactVo.getKeyword().trim();
        // 根据关键字搜索用户是否存在

        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper
                .eq(User::getStatus, AccountStatusEnum.NORMAL.getCode())
                .and(wp -> {
                    wp.eq(User::getEmail, keyword)
                            .or()
                            .eq(User::getLoginNumber, keyword);
                });
        User searchUser = this.getOne(wrapper);

        if (!Objects.nonNull(searchUser)) {
            throw new BusinessException("无法找到该用户，请检查你填写的账号是否正确。");
        }

        return searchUser.getUid();
    }


    /**
     * 获取用户信息
     *
     * @param uid
     * @return
     */
    @Override
    public UserInfoVo getUserInfo(Integer uid) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUid, uid);
        User user = this.getOne(wrapper);
        if (Objects.isNull(user)) {
            throw new BusinessException("无法找到该用户，请检查你填写的账号是否正确。");
        }

        // 查询用户联系人状态
        LambdaQueryWrapper<UserContact> userContactWrapper = new LambdaQueryWrapper<>();

        // 判断当前用户的uid是否小于查询的uid，因为数据库中的uid总是以小的那个存储
        if (uid > ContextUtil.getUserInfo().getUid()) {
            userContactWrapper
                    .eq(UserContact::getUid, ContextUtil.getUserInfo().getUid())
                    .eq(UserContact::getContactId, uid);
        } else {
            userContactWrapper
                    .eq(UserContact::getUid, uid)
                    .eq(UserContact::getContactId, ContextUtil.getUserInfo().getUid());
        }

        userContactWrapper
                .eq(UserContact::getContactType, UserContactTypeEnum.FRIEND.getCode())
                .eq(UserContact::getIsBlocked, UserContactBlockEnum.NOT_BLOCKED.getCode())
                .eq(UserContact::getDeletedBy, 0);


        UserContact userContact = userContactService.getOne(userContactWrapper);

        UserInfoVo userInfoVo = new UserInfoVo();
        BeanUtils.copyProperties(user, userInfoVo);

        userInfoVo.setContactStatus(Objects.nonNull(userContact) && userContact.getContactStatus().equals(UserContactStatus.FRIEND.getCode()) ? 1 : uid.equals(ContextUtil.getUserInfo().getUid()) ? 1 : 0);

        return userInfoVo;
    }

    /**
     * 获取不重复的登录的code，微信要求最大不超过int的存储极限
     * 防止并发，可以给方法加上synchronize，也可以使用cas乐观锁
     *
     * @return
     */
    private Integer generateLoginCode() {
        //本地cache时间必须比redis key过期时间短，否则会出现并发问题
        int inc = RedisUtils.integerInc(RedisKey.getKey("logincode"), 60, TimeUnit.MINUTES);
        //储存一份在本地
        return inc;
    }

    private TokenUserInfoDto getTokenUserInfoDto(User user) {
        TokenUserInfoDto tokenUserInfoDto = new TokenUserInfoDto();
        tokenUserInfoDto.setUid(user.getUid());
        tokenUserInfoDto.setNickName(user.getNickname());

        // TODO

        return tokenUserInfoDto;
    }




}




